home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.4 Applications 1997 August / SGI IRIX 6.4 Applications 1997 August.iso / dist / impr_dev.idb / usr / impressario / src / examples / libstiff / band.c.z / band.c
Encoding:
C/C++ Source or Header  |  1997-07-30  |  13.7 KB  |  545 lines

  1.  /**************************************************************************
  2.  *
  3.  * File: band.c
  4.  *
  5.  * Description: Prints detailed information about a STIFF format file.
  6.  *    The header and IFD information is printed. For each IFD the tags
  7.  *    are printed. Offsets for IFD's and data are also displayed.
  8.  *    If a file is not specified on the command-line, stdin is read.
  9.  *  Converts the file to Banded data. Currently, banded data is not
  10.  *  supported by STIFF.
  11.  *
  12.  *  band.c takes chunky/planar data and converts it to banded data.
  13.  *
  14.  *    With the following inputs,
  15.  *      RGB ( 3 color ) 1 bit per sample, chunky ( with STIFF header )
  16.  *
  17.  *      RGB0RGB0RGB0 ....    _____ End of Line 0
  18.  *      RGB0RGB0RGB0 ....    _____ End of Line 1
  19.  *      ...
  20.  *
  21.  *    the output will be in the following format:
  22.  *      RGB ( 3 color ) 1 bit per sample, banded ( without STIFF header )
  23.  *
  24.  *      RRRRR ....
  25.  *      GGGGG ....
  26.  *      BBBBB ....   _____ End of Line 0
  27.  *      RRRRR ....
  28.  *      GGGGG ....
  29.  *      BBBBB ....   _____ End of Line 1
  30.  *      ...
  31.  *
  32.  *    i.e, the data specific to a color in each line is contiguous.
  33.  *
  34.  *    Usage: band [STIFF file] [outfile]
  35.  *
  36.  **************************************************************************/
  37.  
  38.  
  39. #ident "$Revision: 1.1 $"
  40.  
  41.  
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <unistd.h>
  45. #include <fcntl.h>
  46. #include <string.h>
  47. #include <stiff.h>
  48. #include <printstiff.h>
  49.  
  50. #define    ST_PLANE_BANDED    3
  51.  
  52. #define    MASK4    ((char)0x0f)
  53.  
  54. #define    WR_FLAGS    ( O_CREAT | O_TRUNC | O_WRONLY )
  55.  
  56. #define ROUND(wd, n)    ( (wd) % (n) ? (wd)/(n) + 1 : (wd)/(n) )
  57. #define RND_BYTE(wd)    ( ROUND(wd, 8) )
  58.  
  59. #define    MESSAGE        stderr
  60.  
  61. #define    UMASK        (0644)
  62.  
  63. static int output;
  64.  
  65. static char *progname = NULL;
  66.  
  67. static char *linebuf = NULL;
  68. static int linelen = 0;
  69.  
  70. int ConvertImage(char *buf, PSTImageHeader *ifd);
  71. int PackedToBand(char *buf, PSTImageHeader *ifd);
  72. int PlanarToBand(char *buf, PSTImageHeader *ifd);
  73. int DumpBuffer( int out, void *buf, int size);
  74.  
  75. int ChunkyOneToBand( char *buf, int width, int height, int spp );
  76. int ChunkyFourToBand( char *buf, int width, int height, int spp );
  77. int ChunkyEightToBand( char *buf, int width, int height, int spp );
  78.  
  79. #define    DUMP(num)    fprintf(MESSAGE, "%.2x ", (num))
  80.  
  81. int DumpBuffer( int out, void *buf, int size)
  82. {
  83.     if ( out < 1 || !buf || size <= 0 )
  84.         return 0;
  85.     return write( out, buf, size);
  86. }
  87.  
  88. main(int argc, char *argv[])
  89. {
  90.     STStream *stptr;         /* Stream TIFF pointer to read */
  91.     PSTImageHeader ifd;      /* Struct to get image headers */
  92.     int n = 0;               /* Number of images read so far */
  93.     unsigned long ifdOffset; /* Maintains offset of next IFD */
  94.     int input;               /* input file descriptor */
  95.     char *imgbuf = NULL;     /* Image Buffer */
  96.     int imglen = 0;          /* Image Buffer length */
  97.     int nbytes = 0;
  98.     int first=1;
  99.  
  100.     if ((progname = strrchr(argv[0],'/')) != NULL) progname++;
  101.     else progname = argv[0];
  102.  
  103.     /*
  104.      * input is from file if a file has been specified on the command
  105.      * line, or standard input if no files were specified.
  106.      */
  107.     input = argc > 1 ? open(argv[1], O_RDONLY) : fileno(stdin);
  108.     output = argc > 2 ? open(argv[2], WR_FLAGS, UMASK ) : fileno(stdout);
  109.  
  110.     if (input < 0 || output < 1) {
  111.     perror(progname);
  112.     exit(1);
  113.     }
  114.  
  115.     /*
  116.      * Open a TIFF stream
  117.      */
  118.     if ((stptr = STOpen(input, ST_READ)) == NULL) {
  119.     STPerror(progname);
  120.     exit(1);
  121.     }
  122.  
  123.     /*
  124.      * Print file header information
  125.      */
  126.     (void)fprintf(MESSAGE, "TIFF Header (offset: 0)\n");
  127.     (void)fprintf(MESSAGE, "First IFD at offset: 0x%04lX\n", stptr->next);
  128.     (void)fprintf(MESSAGE, "\n");
  129.  
  130.     /*
  131.      * Read and display IFD information until PSTReadImageHeader
  132.      * returns -1 indicating that no more IFDs can be read from the
  133.      * stream.
  134.      */
  135.     ifdOffset = stptr->next;
  136.     while (PSTReadImageHeader(stptr, &ifd) >= 0) {
  137.     /*
  138.      * Print the offset of this IFD, which we got out of stptr
  139.      * before calling STReadImageHeader
  140.      */
  141.     n++;
  142.     (void)fprintf(MESSAGE, "IFD %d (offset: 0x%04lX)\n", n, ifdOffset);
  143.     /*
  144.      * Now update ifdOffset for next time through the loop.
  145.      */
  146.     ifdOffset = stptr->next;
  147.  
  148.     /*
  149.      * Print the tags
  150.      */
  151.  
  152.     if (STPrintTags(MESSAGE, stptr) < 0) {
  153.         STPerror(progname);
  154.         exit(1);
  155.     }
  156.  
  157.     /*
  158.      * Print the STIFF Image Header structure
  159.      */
  160.     (void)fprintf(MESSAGE, "STIFF Image Header\n");
  161.     (void)fprintf(MESSAGE, "\tImage width: %lu\n", ifd.width);
  162.     (void)fprintf(MESSAGE, "\tImage height: %lu\n", ifd.height);
  163.     (void)fprintf(MESSAGE, "\tBits per sample: %u\n", ifd.bitsPerSample);
  164.     (void)fprintf(MESSAGE, "\tSamples per pixel: %u\n", ifd.samplesPerPixel);
  165.     (void)fprintf(MESSAGE, "\tImage size (bytes): %lu\n", ifd.imgbytes);
  166.     (void)fprintf(MESSAGE, "\tImage type: %u\n", ifd.type);
  167.     (void)fprintf(MESSAGE, "\tData format: %s\n", 
  168.         (ifd.plane == ST_PLANE_PACKED ? "chunky / packed" :
  169.         (ifd.plane == ST_PLANE_PLANAR ? "planar" : "banded")) );
  170.  
  171.     /*
  172.      * Create the buffer for storing the image.
  173.      */
  174.     if ( first ) {
  175.         imglen = ifd.imgbytes;
  176.         imgbuf = (char *)calloc(1, imglen + 1);
  177.         first = 0;
  178.     } else if ( imglen < ifd.imgbytes ) {
  179.         imglen = ifd.imgbytes;
  180.         imgbuf = (char *)realloc(imgbuf, imglen + 1);
  181.     }
  182.  
  183.     /*
  184.      * Convert the input Planar/Chunky image to Band format.
  185.      * And print the output to stdout.
  186.      */
  187.     if ( (nbytes = STRead(stptr, imgbuf, ifd.imgbytes)) < 0 || 
  188.             nbytes < ifd.imgbytes ) {
  189.         fprintf(MESSAGE, " Could not read image data \n");
  190.         break;
  191.     } else {
  192.         ConvertImage(imgbuf, &ifd);
  193.         }
  194.     }
  195.  
  196.     if ( imgbuf )
  197.     free(imgbuf);
  198.     if ( linebuf )
  199.     free(linebuf);
  200.     /*
  201.      * STReadImageHeader will return -1 with STerrno set to STEEOF to
  202.      * indicate that there are no more images.  We don't consider this
  203.      * an error unless no images at all were read in.
  204.      */
  205.     if (STerrno != STEEOF || n == 0) {
  206.     STPerror(progname);
  207.     }
  208.  
  209.     /*
  210.      * Close the TIFF stream
  211.      */
  212.     STClose(stptr);
  213.  
  214.     return 0;
  215. }
  216.  
  217.  
  218. /*
  219.  * RETURNS: -1 EOF, 0 Error, # no. of bytes written.
  220.  */
  221. int ConvertImage(char *buf, PSTImageHeader *ifd)
  222. {
  223.     int totalbytes = 0, nbytes = 0;
  224.  
  225.     switch ( ifd->type ) {
  226.         case ST_TYPE_K:
  227.         case ST_TYPE_W:
  228.  
  229. /*
  230.             ifd->plane = ST_PLANE_BANDED;
  231.             nbytes = DumpBuffer(output, ifd, sizeof(PSTImageHeader));
  232.             if ( nbytes <= 0 ) return nbytes;
  233.             totalbytes += nbytes;
  234. */
  235.             nbytes = DumpBuffer(output, buf, ifd->imgbytes);
  236.             if ( nbytes <= 0 ) return nbytes;
  237.             totalbytes += nbytes;
  238.  
  239.             return totalbytes;
  240.     }
  241.  
  242.     if ( ifd->plane == ST_PLANE_PACKED ) { /* chunky format */
  243.         totalbytes = PackedToBand(buf, ifd);
  244.     } else if ( ifd->plane == ST_PLANE_PLANAR ) { /* planar format */
  245.         totalbytes = PlanarToBand(buf, ifd);
  246.     } else {
  247.         fprintf(MESSAGE, " Unknown data \n");
  248.         return 0;
  249.     }
  250.  
  251.     return totalbytes;
  252. }
  253.  
  254. /*
  255.  * Convert from chunky format to banded format.
  256.  */
  257. int PackedToBand(char *buf, PSTImageHeader *ifd)
  258. {
  259.  
  260.     int spp = ifd->samplesPerPixel;
  261.     int bps = ifd->bitsPerSample;
  262.     int width = ifd->width;
  263.     int height = ifd->height;
  264.     int totalbytes = 0;
  265.     int (*ConvertToBand)(char *, int, int, int);
  266.  
  267.     /*
  268.      * In chunky format, for 3 color 1 bitPerSample, a bit is padded to
  269.      * fit into a nibble. So samplesPerPixel shows as 4 though it is 
  270.      * actually 3. For more than 1 bitPerSample ( 4 & 8 ), no padding is done
  271.      * as long as the data does not end in the middle of a byte.
  272.      *
  273.      * Here, we find out the actual number of samples (colors) per pixel.
  274.      */
  275.     switch ( ifd->type ) {
  276.         case ST_TYPE_RGB:
  277.         case ST_TYPE_YMC:
  278.         case ST_TYPE_CMY:
  279.             spp = 3;
  280.             break;
  281.         case ST_TYPE_YMCK:
  282.         case ST_TYPE_KCMY:
  283.         case ST_TYPE_CMYK:
  284.             spp = 4;
  285.             break;
  286.         default:
  287.             spp = 3;
  288.             break;
  289.     }
  290.  
  291.     width = ROUND(ifd->imgbytes, height);
  292.  
  293.     /*
  294.      * The following computation is only to print the actual banded width.
  295.      * But we pass the width of chunky/planar directly to the sub functions.
  296.      */
  297.     width = spp * ROUND(width, (bps == 1 ? 4 : spp));
  298.     fprintf(MESSAGE, "Banded image width in bytes = %d , %d colors\n", 
  299.                 width, spp );
  300.     fprintf(MESSAGE, "Banded image size in bytes = %d \n", width * height );
  301.  
  302.     width = ROUND(ifd->imgbytes, height);
  303.  
  304.     /*
  305.      * chunky/Packed format has all the 3/4 colors in a nibble ( 4 bits ).
  306.      *
  307.      * We just pick-up the various colors in a nibble and
  308.      * put them in banded format. So for example, if the input
  309.      * is in YCMK format ( Samples Per Pixel = 4 ), we extract
  310.      * all the Y's and put them contiguously so also C, M and K data.
  311.      * So in banded format, first we have Y line, then C, then M and 
  312.      * then K line in that order.
  313.      * For a 1 bitPerSample, 3 samplesPerPixel type format we have only 
  314.      * 3 lines corresponding to each line of the input chunky format. 
  315.      * So in fact the output data will be lesser than chunky format data, 
  316.      * because, we are stripping the padded  bit.
  317.      */
  318.     switch ( bps ) {
  319.         default:
  320.         case 1:
  321.             ConvertToBand = ChunkyOneToBand;
  322.             break;
  323.         case 4:
  324.             ConvertToBand = ChunkyFourToBand;
  325.             break;
  326.         case 8:
  327.             ConvertToBand = ChunkyEightToBand;
  328.             break;
  329.     }
  330.     totalbytes = ConvertToBand( buf,
  331.                             width,
  332.                             height,
  333.                             spp );
  334.  
  335.     return totalbytes;
  336. }
  337.  
  338. int ChunkyOneToBand( char *buf, int width, int height, int ncolors )
  339. {
  340.     int mask=0, offset=0, color[4];
  341.     int count=0, nbytes=0, totalbytes=0;
  342.     int i, j, k;
  343.  
  344.     /*
  345.      * width represents the width of all the colors put together.
  346.      */
  347.     offset = ROUND(width, 4);
  348.  
  349.     if ( !linebuf ) {
  350.         linelen = ncolors * offset;
  351.         linebuf = (char *)calloc(1, linelen);
  352.     } else if ( linelen < ncolors * offset ) {
  353.         linelen = ncolors * offset;
  354.         linebuf = (char *)realloc(linebuf, linelen);
  355.     }
  356.  
  357.     /*
  358.      * In the case of 1 bit per sample, the input byte contains 2 samples.
  359.      * But when the no. of colors used is 3, padding is done to fill
  360.      * the nibble. So we mask out the padded bit when 'ncolors' is 3.
  361.      */
  362.     mask = ncolors == 3 ? 0x02 : 0x01;
  363.  
  364.     color[0] = color[1] = color[2] = color[3] = 0;
  365.  
  366.     while ( height-- ) {
  367.         for ( count = i = 0; i < width; count++, i += 4 ) {
  368.         /*
  369.          * Extract the bits from 4 bytes of the input buffer.
  370.          * This makes a full byte because, each input buffer byte
  371.          * contains 2 bits of a particular color.
  372.          */
  373.         for ( j = 0; j < 4; j++ ) {
  374.             register int byte = buf[i+j];
  375.  
  376.             /*
  377.              * Extract the bits for each color.
  378.              * ex: for CMY, k=0 ==> Y & k=2 ==> C.
  379.              * and color[0] is C ... color[2] is Y.
  380.              */
  381.             for ( k=0; k < ncolors; k++ ) {
  382.             register int b = color[ncolors - k - 1];
  383.  
  384.             /* first time we expect 'b' to be 0 */
  385.             b <<= 1;
  386.             b |= ( (byte & ((mask << k) << 4 )) ? 
  387.                     0x01 : 0x00 );
  388.             b <<= 1;
  389.             b |= ( (byte & (mask << k)) ? 
  390.                     0x01 : 0x00 );
  391.             color[ncolors - k - 1] = b;
  392.             }
  393.         }
  394.  
  395.         /*
  396.          * Bytes for all the colors have been formed.
  397.          * So copy them into 'linebuf' & init them back to 0.
  398.          * The first color starts at location 0, 2nd at '1*offset'...
  399.          */
  400.         for ( k=0; k < ncolors; color[k++] = 0 )
  401.             linebuf[count+k*offset] = color[k];
  402.         }
  403.  
  404.         if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
  405.             return nbytes;
  406.         totalbytes += nbytes;
  407.         buf += width;
  408.     }
  409.  
  410.     return totalbytes;
  411.  
  412. }
  413.  
  414. int ChunkyFourToBand( char *buf, int width, int height, int ncolors )
  415. {
  416.     int nbytes=0, totalbytes=0;
  417.     int offset = 0;
  418.     int nbits=4, nsamples=0;
  419.     int index = 0, count = 0;
  420.     int i, j, k;
  421.     register char mask = MASK4;
  422.     register char byte=0;
  423.     register char nib=0;
  424.  
  425.     nsamples = (width * (8/nbits))/ncolors;
  426.     offset = ROUND( nbits*nsamples, 8 );
  427.  
  428.     if ( !linebuf ) {
  429.         linelen = ncolors * offset;
  430.         linebuf = (char *)calloc(1, linelen);
  431.     } else if ( linelen < ncolors * offset ) {
  432.         linelen = ncolors * offset;
  433.         linebuf = (char *)realloc(linebuf, linelen);
  434.     }
  435.  
  436.     while ( height-- ) {
  437.         for ( j = 0; j < ncolors; j++ ) {
  438.  
  439.             index = count = byte = 0;
  440.             mask = j%2 ? MASK4 : ~MASK4;
  441.  
  442.             for ( i = 0; i < width && count < nsamples ; byte=0) {
  443.                 for ( k = 0; count < nsamples && k < 2; nib=0, k++ ) {
  444.                     nib = buf[i] & mask;
  445.                     nib = mask & MASK4 ? nib : MASK4 & (nib >> nbits);
  446.                     byte |= nib;
  447.                     if ( !k )
  448.                         byte <<= nbits;
  449.                     i = nbits * ( j + ++count * ncolors )/8;
  450.                     mask = ~mask;
  451.                 }
  452.                 linebuf[index++ + offset * j] = byte;
  453.             }
  454.         }
  455.         if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
  456.             return nbytes;
  457.         totalbytes += nbytes;
  458.         buf += width;
  459.     }
  460.  
  461.     return totalbytes;
  462. }
  463.  
  464. int ChunkyEightToBand( char *buf, int width, int height, int ncolors )
  465. {
  466.     int nbytes=0, totalbytes=0;
  467.     int offset = 0, nsamples=0;
  468.     int j, count=0;
  469.  
  470.     offset = nsamples = ROUND(width, ncolors);
  471.  
  472.     if ( !linebuf ) {
  473.         linelen = ncolors * offset;
  474.         linebuf = (char *)calloc(1, linelen);
  475.     } else if ( linelen < ncolors * offset ) {
  476.         linelen = ncolors * offset;
  477.         linebuf = (char *)realloc(linebuf, linelen);
  478.     }
  479.  
  480.     while ( height-- ) {
  481.         for ( j = 0; j < ncolors; j++ ) {
  482.             for ( count = 0; count < nsamples; count++ ) 
  483.                 linebuf[count + offset*j] = buf[j + count*ncolors];
  484.         }
  485.         if ( (nbytes = DumpBuffer(output, linebuf, ncolors * offset)) <= 0 )
  486.             return nbytes;
  487.         totalbytes += nbytes;
  488.         buf += width;
  489.     }
  490.  
  491.     return totalbytes;
  492. }
  493.  
  494.  
  495. /*
  496.  * Convert from planar format to banded format.
  497.  */
  498. int PlanarToBand(char *buf, PSTImageHeader *ifd)
  499. {
  500.  
  501.     int spp = ifd->samplesPerPixel;
  502.     int width = ifd->width;
  503.     int height = ifd->height;
  504.     int nbytes=0, totalbytes=0;
  505.     int i=0, j=0;
  506.  
  507.     /*
  508.      * It's always better to resolve the spp.
  509.      * So, here we find out the actual number of samples (colors) per pixel.
  510.      */
  511.     switch ( ifd->type ) {
  512.         case ST_TYPE_RGB:
  513.         case ST_TYPE_YMC:
  514.         case ST_TYPE_CMY:
  515.             spp = 3;
  516.             break;
  517.         case ST_TYPE_YMCK:
  518.         case ST_TYPE_KCMY:
  519.         case ST_TYPE_CMYK:
  520.             spp = 4;
  521.             break;
  522.         default:
  523.             spp = 3;
  524.             break;
  525.     }
  526.  
  527.     width = ROUND(ifd->imgbytes, height);
  528.  
  529.     fprintf(MESSAGE, "Banded image width in bytes = %d , %d colors\n", 
  530.                 width, spp );
  531.     fprintf(MESSAGE, "Banded image size in bytes = %d \n", width * height );
  532.  
  533.     width = ROUND(width, spp);
  534.  
  535.     for ( i=0; i < height; i++ ) {
  536.         for ( j = 0; j < spp; j++ ) {
  537.             nbytes = DumpBuffer(output, &buf[j*width*height + i], width);
  538.             if ( nbytes <= 0 ) return nbytes;
  539.             totalbytes += nbytes;
  540.         }
  541.     }
  542.     return totalbytes;
  543. }
  544.  
  545.